You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Use the async Always Encrypted key store APIs on async execution paths - #4585
SqlColumnEncryptionKeyStoreProvider gained async APIs in #4540, but the driver did not call them. This PR wires those APIs into SqlCommand parameter-encryption and enclave-package key loading, so asynchronous command execution can await key-store I/O.
Async utility layer
Adds async counterparts to SqlSecurityUtility.DecryptSymmetricKey, GetKeyFromLocalProviders, VerifyColumnMasterKeySignature, AlwaysEncrypted.SymmetricKeyCache.GetKey, and enclave-package key loading/generation.
Async cold-cache misses use a per-key gate and cache recheck to avoid duplicate unwraps. Provider invocation shares the existing synchronous-provider gate, preserving serialization for legacy providers whose base async method calls synchronous code. The returned task is awaited outside that gate, allowing outstanding async I/O for different keys to overlap. Cache publication uses a separate short in-memory lock; sync/async races retain first-published key identity.
Validation, provider error wrapping, cache behavior, and multi-key fallback are preserved. No public API changes. Synchronous callers retain synchronous provider dispatch.
Call site integration
Sync and async describe-parameter-encryption paths share one parser. ReadDescribeEncryptionParameterResultsCore records signature verifications and key decryptions in PendingColumnEncryptionKeyOperations; sync and async wrappers then execute that work after parsing.
Enclave-package generation and post-describe execution gain async counterparts using shared validation and package-building helpers. Deferred retryable package faults use the existing cache/session invalidation and bounded retry handler.
Cancellation
Reader, NonQuery, and XML async entry points capture the caller token, pass it to key-store operations, and clear it during execution cleanup. Cancellation after describe-phase state release avoids accessing a released state object. Cancellation takes precedence over candidate/CMK validation and provider errors. CMK verification checks cancellation before accepting true/false results or publishing signature-cache entries. Successful key decryption still publishes its result when cancellation arrives afterward.
Query metadata cache
Cache lookup is split into an in-memory probe and an awaitable key-loading completion. Async completion is dispatched to a worker so synchronously completed provider tasks cannot run the stale-cache describe fallback on the caller thread. Sync lookup uses the same probe without changing its dispatch behavior.
Notes for reviewers
usedCache remains conservatively over-reported on stale-key describe fallback, preserving the existing single retry for conversion errors.
Task.Run is deliberate at boundaries that can issue blocking TDS work, including cached-key completion when providers complete synchronously.
Provider errors retain their existing wrapping; caller cancellation remains an OperationCanceledException.
Human review threads remain open after replies, as requested.
Behaviour notes worth a careful look
Exception ordering on malformed describe results. Parsing finishes before key-store work. If results are malformed and contain an invalid key, the parsing error can now surface first.
Network callback threads. Describe-result and post-describe work retain their worker dispatches rather than performing blocking TDS work on SNI callbacks.
Sequential key loading. Distinct metadata entries can share a key; sequential loading lets the first result warm the cache without duplicate provider I/O.
Scope
This PR covers SqlCommand parameter-encryption and enclave-package key loading, including Reader, NonQuery, and XML async execution. Async enclave attestation is separate (#4886/#4887); those branches were not imported. Session lookup may still perform synchronous attestation I/O, as the enclave-package remarks state.
SqlBulkCopy.WriteToServerAsync still reaches synchronous key decryption through CopyBatchesAsync / TdsParser.LoadColumnEncryptionKeys. Those implementations are unchanged and remain deferred work under #3672.
Regression coverage includes sync/async provider dispatch, multi-key fallback, cancellation and command reuse, CMK signature-cache cancellation, metadata-cache completion, deferred package retry outcomes, same-key cold-cache deduplication, independent keys, and sync/async publication races.
Latest fix 284463de5 restores legacy synchronous-provider serialization without holding the gate across outstanding async I/O. Both new regression cases failed before the fix and pass afterward: a distinct-key waiter cancels without entering the blocked synchronous provider, then succeeds on retry. Earlier 3ec157d48 moved intentionally blocking test-provider work to a dedicated thread and finally-owned release, preserving completion and identity assertions.
All eight cases were discovered and skipped because local AE configuration is unavailable. Server-side enclave retry validation remains outstanding. A prior full local unit run aborted in simulated-routing teardown; the isolated case also hangs on unchanged main 616aeff04. No routing tests were changed or disabled.
Current-head CI build 188785, through merge 995d74c410171d423366b766df90cf67a515a2af: all three unit and all three AE tasks passed. Linux Azure manual tests encountered schema-discovery deadlocks; Linux localhost manual tests encountered unsupported distributed-transaction promotion in a pool-reuse test. One failed-job-only Linux retry is in progress; passing jobs were not rerun.
Build 188783 exhausted its two automatic Windows Azure net462 manual-job retries. All failures were schema-discovery deadlocks in AddTVPsToDataTypesTableAsync, including vector metadata tests using GetSchema. These unchanged paths do not invoke key providers. The user explicitly requested leaving these failures outside this PR; no unrelated fixes, weaker assertions, or further retries were applied.
The milestone remains 8.1.0-preview1 by explicit user decision. Main's active line and SqlClientNextVersion remain 8.0.0-preview1, so the two milestone checks remain intentionally blocked. No milestone, release version, or check configuration was changed to bypass the gate.
Guidelines
Please review the contribution guidelines before submitting a pull request:
Async command execution now resolves Always Encrypted key material through
the asynchronous key store provider APIs, so a provider that performs network
I/O (for example Azure Key Vault) no longer blocks a thread pool thread for the
duration of every column encryption key decryption, column master key signature
verification, and enclave package generation.
Utility layer (Phase 4):
* SqlSecurityUtility gains DecryptSymmetricKeyAsync, GetKeyFromLocalProvidersAsync
and VerifyColumnMasterKeySignatureAsync.
* SqlSymmetricKeyCache gains GetKeyAsync, which never holds the cache lock across
provider I/O.
Call sites (Phase 5):
* sp_describe_parameter_encryption result parsing is now shared by the sync and
async paths. Signature verifications and key decryptions are recorded while
parsing and executed afterwards, so no key store call is made while the reader
is still positioned mid-stream.
* The two callback-based describe-parameter-encryption continuations collapse
into a single async method, preserving the existing thread pool hand-off and
error/cancellation semantics.
* EnclaveDelegate and SqlCommand gain async enclave package generation, used by
the async execution path.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
… store calls
The async key store provider APIs all accept a CancellationToken, but the
Always Encrypted call sites had no way to reach the token supplied to
ExecuteReaderAsync / ExecuteNonQueryAsync / ExecuteXmlReaderAsync, so key
store I/O (for example an Azure Key Vault round trip) could not be cancelled.
Capture the token in a per-execution field on SqlCommand, set at each async
entry point and cleared by the matching cleanup continuation, and pass it to
the describe-parameter-encryption key operations and to enclave package
generation. A field is used rather than signature plumbing because the token
would otherwise have to be threaded through several sync-only overloads of
RunExecuteReader and PrepareForTransparentEncryption.
Also add a manual test that registers a recording key store provider and
asserts that the synchronous execution paths use the synchronous provider
API, the asynchronous paths use the asynchronous provider API, and that
cancelling the caller's token cancels the key store operation.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 82aca8ca-2078-4a47-860c-6f6ee78cbb02
On a query metadata cache hit the driver still loaded every column encryption
key synchronously, on the caller's thread, and then generated the enclave
package with the synchronous key store provider API. Both can perform key
store network I/O, so an ExecuteReaderAsync that hit the metadata cache could
still block the caller.
Split the cache lookup into an in-memory probe (TryGetCachedQueryMetadata),
which matches the cached cipher metadata onto the parameters and reports which
keys still need decrypting, and a completion step that loads those keys. The
synchronous lookup is now expressed in terms of the same probe, so its
behaviour, including the stale key fallback and the cache hit/miss counters, is
unchanged.
Asynchronous execution now takes the probe and returns a task for the key
loads, which means the cache hit path produces a describe-parameter-encryption
task exactly like the cache miss path does. As a result it also picks up the
asynchronous enclave package generation and no longer issues the command's TDS
write from the caller's thread.
When the cached key information turns out to be stale, that task falls back to
a full describe parameter encryption round trip. The command still reports
usedCache = true in that case because the caller has already returned; this is
deliberately conservative, since over-reporting a cache hit can only cost one
extra retry of an already failing execution, whereas under-reporting it would
suppress the retry a genuine cache hit needs.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 82aca8ca-2078-4a47-860c-6f6ee78cbb02
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR wires the async SqlColumnEncryptionKeyStoreProvider APIs into the Always Encrypted async execution paths so ExecuteReaderAsync / ExecuteNonQueryAsync / ExecuteXmlReaderAsync no longer block threads on key store network I/O (e.g., Azure Key Vault), while preserving sync behavior and error semantics.
Changes:
Adds async counterparts in the Always Encrypted utility/enclave layers (e.g., SqlSecurityUtility.DecryptSymmetricKeyAsync, SqlSymmetricKeyCache.GetKeyAsync, EnclaveDelegate.GenerateEnclavePackageAsync).
Refactors describe-parameter-encryption processing to share parsing between sync/async and to defer key-store operations until after result-set parsing.
Flows the caller’s CancellationToken into AE key-store operations via per-execution state on SqlCommand, and updates query metadata cache to split probe vs. async completion.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.
GetParameterEncryptionDataReader returned void with an out Task and did
nothing but dispatch asynchronous work, so its name gave no hint that the
caller was receiving a pending operation. The name was inherited from main,
where it was actively misleading: the overload without the Async suffix was
the one that continued from a pending network write, while the overload with
the suffix was the one used when that write had already completed.
Rename the dispatcher to GetParameterEncryptionDataReaderAsync and have it
return the task directly instead of through an out parameter, and rename the
async body to ConsumeDescribeParameterEncryptionResultsAsync, which describes
what it actually does. Both names now carry the Async suffix and both return
a task.
Also make the Task.Run rationale precise. It is not merely a convenience: the
body issues blocking TDS reads before it reaches a suspension point, and
PrepareForTransparentEncryption runs synchronously on the caller's thread
under the async entry points, so awaiting inline would put those reads on the
caller's thread whenever the network write is null or already complete.
Neither continuation this replaced could run inline, since one used Task.Run
and the other used ContinueWith without ExecuteSynchronously.
No behaviour change.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 82aca8ca-2078-4a47-860c-6f6ee78cbb02
_asyncExecutionCancellationToken is documented as representing the token “currently in flight”, but it looks like it’s only cleared in the async cleanup continuations. If an async entry point throws during setup (e.g., Task.Factory.FromAsync throws before the continuation runs), the token can remain set on the SqlCommand instance, unnecessarily retaining the caller’s CancellationTokenSource and violating the stated invariant. Consider clearing this field in the synchronous exception paths of InternalExecuteReaderAsync / InternalExecuteNonQueryAsync / InternalExecuteXmlReaderAsync as well (before returning a faulted Task).
/// <summary>
/// Cancellation token supplied by the caller of the asynchronous execution that is currently in
/// flight, or <see cref="CancellationToken.None"/> when the command is executing synchronously.
/// </summary>
/// <remarks>
Report CreateColumnEncryptionKeyInfo as the throwing member in the
internal null-argument diagnostics. The throws moved into that shared
helper and are now reachable from both the sync and async decrypt
paths, so naming GetDecryptedKeysToBeSentToEnclave was misleading. This
also matches the convention used by EncryptBytePackage and
ComputeQueryStringHash in the same type.
Use TdsEnums.AEAD_AES_256_CBC_HMAC_SHA256 and
SqlClientEncryptionType.Deterministic in the query metadata cache tests
instead of bare literals with explanatory comments.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 82aca8ca-2078-4a47-860c-6f6ee78cbb02
SqlCommand (and its underlying SqlConnection) should be disposed in this test to avoid leaking resources across the unit test process; use a using declaration for the cached command instance.
This issue also appears in the following locations of the same file:
This test creates a SqlCommand with an owned SqlConnection but never disposes it; prefer a using declaration so the connection/command are cleaned up even if the test fails.
A pass over the async Always Encrypted work looking for correctness,
performance, and consistency problems. The behavioural changes:
Capture the cancellation token on the originating thread. The token lives
in a field so it does not have to be threaded through layers of
callback-driven code that have no use for it, but the Always Encrypted
work is handed to the thread pool and can outlive the cleanup
continuation that resets that field. Reading it late could silently
observe CancellationToken.None. It is now read once at the two
synchronous capture points and passed on as an explicit parameter.
Make the symmetric key cache insert non-cancellable. Cancelling between
a successful decrypt and the cache insert threw away a key that had
already cost a key store round trip, so the next caller paid for it
again.
Drop a dead serverName parameter from both GetDecryptedKeysToBeSentToEnclave
overloads, and give the result list a capacity hint.
The rest is allocation and clarity work: lazily allocate the pending key
operation lists and hand them out as IReadOnlyList, walk them by index so
the interface enumerator is not boxed, and make IsEnclaveEnabled an
explicit field on ColumnMasterKeySignatureVerification rather than a
hard-coded true that only happens to be correct because of where the call
sits.
Also documents three decisions that look like missed optimisations but
are not. The Task.Run in GetParameterEncryptionDataReaderAsync cannot be
awaited inline because the task it waits on is completed by the SNI
network callback, so an inline continuation would run blocking TDS reads
on a callback thread. Column encryption key decryption stays sequential
because distinct cipher metadata entries often share a key, and
GetKeyAsync does not hold its gate across provider I/O, so parallel
misses for the same key would each issue their own key store call.
Sequential decryption lets the first result warm the cache.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 82aca8ca-2078-4a47-860c-6f6ee78cbb02
CompleteCachedQueryMetadataAsync treated every exception from the column
encryption key load as evidence that the cached metadata was stale, and
invalidated the entry. Cancellation is not evidence of anything of the
sort, so cancelling an execution threw away a perfectly good cache entry
and made the next caller pay for another describe parameter encryption
round trip.
Cancellation now propagates without touching the cache. This failure
mode cannot arise on the synchronous path, so only the asynchronous
overload handles it and the two stay otherwise identical.
The existing cancellation test only asserted that the operation threw,
which is why this went unnoticed. It now also asserts that the entry
survives and that a subsequent uncancelled load still succeeds, and it
fails without the fix.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 82aca8ca-2078-4a47-860c-6f6ee78cbb02
The comment says the provider exception will be “simply bubbled up”, but this method wraps non-cancellation failures in SQL.KeyDecryptionFailed. Please update the comment so it matches the actual error behavior (only OperationCanceledException is propagated unwrapped).
// Decrypt the CEK
// We will simply bubble up the exception from the DecryptColumnEncryptionKeyAsync function.
byte[] plaintextKey;
The comment says the provider exception will be “simply bubbled up”, but this helper wraps non-cancellation failures in SQL.KeyDecryptionFailed. Please update the comment so it matches the actual behavior (only OperationCanceledException is propagated unwrapped).
// Decrypt the CEK
// We will simply bubble up the exception from the DecryptColumnEncryptionKeyAsync function.
byte[] plaintextKey;
The required sqlclient-pr pipeline is failing only on tests unrelated to
this change, in a pipeline with a ~48% baseline failure rate across other
PRs. Observed failures, none of which touch any file in this PR:
MARSTest.MarsScenarioClientJoin (also failed on PR #4567)
SqlCommandCancelTest.TimeOutDuringRead_Tcp (timing sensitive)
TransactionEnlistmentTest.TestManualEnlistment_Enlist
(also failed on PR #4585)
The /azp run comment trigger is not enabled on this repo, so refreshing
the head SHA is the only available way to re-run the required checks.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cfc64bc6-a9e6-490d-88b1-4b78d25aa103
issue (P2, async): Preserve cancellation when the final candidate fails
If the provider for the last (or only) candidate cancels the caller token but returns a normal exception, the catch records that exception and the loop ends; this branch then rethrows lastException without another cancellation check. That masks caller cancellation as SQL.KeyDecryptionFailed/a validation error, unlike the documented async contract and the multi-candidate case handled by the next-iteration check. Recheck cancellationToken immediately before rethrowing the final candidate error.
This issue also appears on line 617 of the same file.
Observe caller cancellation before rethrowing the last CEK candidate error or wrapping a CMK verification exception. Cover immediate and deferred failures with single and multiple candidates.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 038a9db2-f9a4-45c3-a80a-73cfe8a3bc9f
Preserve cancellation when the final candidate fails
Fixed the review-body finding in 2b5aabd: cancellation now wins before rethrowing the final CEK error and before wrapping a CMK verification exception. Six immediate/deferred failure regressions failed before the fix; all 85 focused tests now pass in Debug and Release.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
The changes span asynchronous encryption, caching, cancellation, and enclave execution paths, while new-head CI and enclave-backed retry validation remain pending.
Keep synchronous provider blocking off the thread pool and let the test orchestration own its release without a competing provider watchdog.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 038a9db2-f9a4-45c3-a80a-73cfe8a3bc9f
Serialize provider invocation with the existing semaphore, releasing it before awaiting returned tasks. Cover synchronous and base-async owners plus cancellation while waiting without blocking completed-key publication.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 038a9db2-f9a4-45c3-a80a-73cfe8a3bc9f
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
The broad, high-risk async command and enclave state changes lack current-head CI and enclave-enabled integration validation, leaving end-to-end retry, cancellation, and cleanup behavior unverified.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Hotfix 8.0.0PRs that must be backported to release/8.0
5 participants
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
SqlColumnEncryptionKeyStoreProvidergained async APIs in #4540, but the driver did not call them. This PR wires those APIs intoSqlCommandparameter-encryption and enclave-package key loading, so asynchronous command execution can await key-store I/O.Async utility layer
Adds async counterparts to
SqlSecurityUtility.DecryptSymmetricKey,GetKeyFromLocalProviders,VerifyColumnMasterKeySignature,AlwaysEncrypted.SymmetricKeyCache.GetKey, and enclave-package key loading/generation.Async cold-cache misses use a per-key gate and cache recheck to avoid duplicate unwraps. Provider invocation shares the existing synchronous-provider gate, preserving serialization for legacy providers whose base async method calls synchronous code. The returned task is awaited outside that gate, allowing outstanding async I/O for different keys to overlap. Cache publication uses a separate short in-memory lock; sync/async races retain first-published key identity.
Validation, provider error wrapping, cache behavior, and multi-key fallback are preserved. No public API changes. Synchronous callers retain synchronous provider dispatch.
Call site integration
Sync and async describe-parameter-encryption paths share one parser.
ReadDescribeEncryptionParameterResultsCorerecords signature verifications and key decryptions inPendingColumnEncryptionKeyOperations; sync and async wrappers then execute that work after parsing.Enclave-package generation and post-describe execution gain async counterparts using shared validation and package-building helpers. Deferred retryable package faults use the existing cache/session invalidation and bounded retry handler.
Cancellation
Reader, NonQuery, and XML async entry points capture the caller token, pass it to key-store operations, and clear it during execution cleanup. Cancellation after describe-phase state release avoids accessing a released state object. Cancellation takes precedence over candidate/CMK validation and provider errors. CMK verification checks cancellation before accepting true/false results or publishing signature-cache entries. Successful key decryption still publishes its result when cancellation arrives afterward.
Query metadata cache
Cache lookup is split into an in-memory probe and an awaitable key-loading completion. Async completion is dispatched to a worker so synchronously completed provider tasks cannot run the stale-cache describe fallback on the caller thread. Sync lookup uses the same probe without changing its dispatch behavior.
Notes for reviewers
usedCacheremains conservatively over-reported on stale-key describe fallback, preserving the existing single retry for conversion errors.Task.Runis deliberate at boundaries that can issue blocking TDS work, including cached-key completion when providers complete synchronously.OperationCanceledException.Behaviour notes worth a careful look
Exception ordering on malformed describe results. Parsing finishes before key-store work. If results are malformed and contain an invalid key, the parsing error can now surface first.
Network callback threads. Describe-result and post-describe work retain their worker dispatches rather than performing blocking TDS work on SNI callbacks.
Sequential key loading. Distinct metadata entries can share a key; sequential loading lets the first result warm the cache without duplicate provider I/O.
Scope
This PR covers
SqlCommandparameter-encryption and enclave-package key loading, including Reader, NonQuery, and XML async execution. Async enclave attestation is separate (#4886/#4887); those branches were not imported. Session lookup may still perform synchronous attestation I/O, as the enclave-package remarks state.SqlBulkCopy.WriteToServerAsyncstill reaches synchronous key decryption throughCopyBatchesAsync/TdsParser.LoadColumnEncryptionKeys. Those implementations are unchanged and remain deferred work under #3672.Issues
Part of #3672, following #4540.
Testing
Regression coverage includes sync/async provider dispatch, multi-key fallback, cancellation and command reuse, CMK signature-cache cancellation, metadata-cache completion, deferred package retry outcomes, same-key cold-cache deduplication, independent keys, and sync/async publication races.
Latest fix
284463de5restores legacy synchronous-provider serialization without holding the gate across outstanding async I/O. Both new regression cases failed before the fix and pass afterward: a distinct-key waiter cancels without entering the blocked synchronous provider, then succeeds on retry. Earlier3ec157d48moved intentionally blocking test-provider work to a dedicated thread andfinally-owned release, preserving completion and identity assertions.Local validation on
284463de5:Passed: 87 tests in Debug and 87 in Release.
Both passed with zero warnings/errors.
Manual run on earlier head
3b10197ce:All eight cases were discovered and skipped because local AE configuration is unavailable. Server-side enclave retry validation remains outstanding. A prior full local unit run aborted in simulated-routing teardown; the isolated case also hangs on unchanged main
616aeff04. No routing tests were changed or disabled.Current-head CI build 188785, through merge
995d74c410171d423366b766df90cf67a515a2af: all three unit and all three AE tasks passed. Linux Azure manual tests encountered schema-discovery deadlocks; Linux localhost manual tests encountered unsupported distributed-transaction promotion in a pool-reuse test. One failed-job-only Linux retry is in progress; passing jobs were not rerun.Build 188783 exhausted its two automatic Windows Azure net462 manual-job retries. All failures were schema-discovery deadlocks in
AddTVPsToDataTypesTableAsync, including vector metadata tests usingGetSchema. These unchanged paths do not invoke key providers. The user explicitly requested leaving these failures outside this PR; no unrelated fixes, weaker assertions, or further retries were applied.The milestone remains 8.1.0-preview1 by explicit user decision. Main's active line and
SqlClientNextVersionremain8.0.0-preview1, so the two milestone checks remain intentionally blocked. No milestone, release version, or check configuration was changed to bypass the gate.Guidelines
Please review the contribution guidelines before submitting a pull request: